import java.awt.Color;
import java.util.ArrayList;

import uchicago.src.reflector.RangePropertyDescriptor;
import uchicago.src.sim.analysis.DataSource;
import uchicago.src.sim.analysis.OpenSequenceGraph;
import uchicago.src.sim.analysis.Sequence;
import uchicago.src.sim.engine.BasicAction;
import uchicago.src.sim.engine.Schedule;
import uchicago.src.sim.engine.SimInit;
import uchicago.src.sim.engine.SimModelImpl;
import uchicago.src.sim.event.SliderListener;
import uchicago.src.sim.gui.ColorMap;
import uchicago.src.sim.gui.DisplaySurface;
import uchicago.src.sim.gui.Object2DDisplay;
import uchicago.src.sim.gui.Value2DDisplay;
import uchicago.src.sim.util.SimUtilities;

/**
 * Class that implements the simulation model for the rabbits grass
 * simulation.  This is the first class which needs to be setup in
 * order to run Repast simulation. It manages the entire RePast
 * environment and the simulation.
 *
 * @author 
 */


public class RabbitsGrassSimulationModel extends SimModelImpl {		

	private static final int NUM_RABBITS=100;
	private static final int X_SIZE=40;
	private static final int Y_SIZE=40;
	private static final int BIRTH_THRES=40;
	private static final int GRASS_RATE=50;
	private static final int RABBIT_MIN_START_ENERGY=35;
	private static final int RABBIT_MAX_START_ENERGY=45;
	private static final int MAX_GRASS_VALUE=16;


	private int numRabbits=NUM_RABBITS;
	private int xSize=X_SIZE;
	private int ySize=Y_SIZE;
	private int birthThres=BIRTH_THRES;
	private int grassRate=GRASS_RATE;
	private int rabbitMinStartEnergy=RABBIT_MIN_START_ENERGY;
	private int rabbitMaxStartEnergy=RABBIT_MAX_START_ENERGY;

	private RabbitsGrassSimulationSpace space;

	private ArrayList agentList;

	private DisplaySurface displaySurf;

	private Schedule schedule;

	private OpenSequenceGraph amountOfGrassInSpace;

	private OpenSequenceGraph amountOfRabbitsInSpace;

	class grassInSpace implements DataSource, Sequence {

		public Object execute() {
			return new Double(getSValue());
		}

		public double getSValue() {
			return (double)space.getTotalGrass();
		}
	}

	class rabbitsInSpace implements DataSource, Sequence {

		public Object execute() {
			return new Double(getSValue());
		}


		public double getSValue() {
			return (double)agentList.size();
		}
	}
	
	class grassRateSlider extends SliderListener {
		  
		/*public grassRateSlider(int firstValue) {
			super();
			//value=firstValue;
			setFirstVal(GRASS_RATE);
		}*/
		
		public void execute() {
		    if (isSlidingLeft) {
		      grassRate = value;
		    } else {
		      grassRate = value;
		    }
		    
		    System.out.println(value);
		  }
		 
		  
		};


	public static void main(String[] args) {

		System.out.println("Rabbit skeleton");
		SimInit init = new SimInit();
		RabbitsGrassSimulationModel model = new RabbitsGrassSimulationModel();
		init.loadModel(model, "", false);

	}


	private void addNewAgent(){
		RabbitsGrassSimulationAgent a = new RabbitsGrassSimulationAgent(rabbitMinStartEnergy,rabbitMaxStartEnergy);

		boolean check=space.addAgent(a);
		// if no place , do not add to the list
		if(check)
			agentList.add(a);
	}

	public void begin() {
		// TODO Auto-generated method stub

		buildModel();

		buildSchedule();

		buildDisplay();

		displaySurf.display();

		amountOfGrassInSpace.display();

		amountOfRabbitsInSpace.display();

	}

	public void buildModel()
	{
		space = new RabbitsGrassSimulationSpace(xSize, ySize);						
		space.createField(xSize,ySize);
		growGrass();

		for(int i = 0; i < numRabbits; i++){
			addNewAgent();
		}

		for(int i = 0; i < agentList.size(); i++){
			RabbitsGrassSimulationAgent cda = (RabbitsGrassSimulationAgent)agentList.get(i);
			cda.report();
		}

	}


	public String[] getInitParam(){
		String[] initParams = { "NumRabbits", "XSize","YSize","BirthThres","GrassRate",
				"RabbitMinStartEnergy","RabbitMaxStartEnergy"};
		return initParams;
	}

	public int getNumRabbits(){
		return numRabbits;
	}

	public void setNumRabbits(int nr){
		numRabbits = nr;
	}

	public int getXSize(){
		return xSize;
	}

	public void setXSize(int xs){
		xSize = xs;
	}

	public int getYSize(){
		return ySize;
	}

	public void setYSize(int ys){
		ySize = ys;
	}
	public int getBirthThres(){
		return birthThres;
	}

	public void setBirthThres(int bt){
		birthThres = bt;
	}

	public int getGrassRate(){
		return grassRate;
	}

	public void setGrassRate(int gr){
		grassRate = gr;
	}

	public int getRabbitMinStartEnergy()
	{
		return rabbitMinStartEnergy;
	}

	public void setRabbitMinStartEnergy(int i)
	{
		rabbitMinStartEnergy=i;
	}

	public int getRabbitMaxStartEnergy()
	{
		return rabbitMaxStartEnergy;
	}

	public void setRabbitMaxStartEnergy(int i)
	{
		rabbitMaxStartEnergy=i;
	}

	public String getName() {
		// TODO Auto-generated method stub
		return null;
	}

	public Schedule getSchedule() {
		// TODO Auto-generated method stub
		return schedule;
	}

	public void setup() {
		// TODO Auto-generated method stub
		space = null;
		agentList = new ArrayList();
		schedule = new Schedule(1);

		if (displaySurf != null){
			displaySurf.dispose();
		}
		displaySurf = null;

		if (amountOfGrassInSpace != null){
			amountOfGrassInSpace.dispose();
		}
		amountOfGrassInSpace = null;

		if (amountOfRabbitsInSpace != null){
			amountOfRabbitsInSpace.dispose();
		}
		amountOfRabbitsInSpace = null;


		displaySurf = new DisplaySurface(this, "Rabbits Model Window 1");
		amountOfGrassInSpace = new OpenSequenceGraph("Amount Of Grass In Space",this);
		amountOfRabbitsInSpace = new OpenSequenceGraph("Amount Of Rabbits In Space",this);

		registerDisplaySurface("Rabbits Model Window 1", displaySurf);
		this.registerMediaProducer("Plot", amountOfGrassInSpace);
		this.registerMediaProducer("Plot", amountOfRabbitsInSpace);
		
		modelManipulator.init();
		
		grassRateSlider slider=new grassRateSlider();
		slider.setFirstVal(GRASS_RATE);
		modelManipulator.addSlider("GrassRate", 0, 1000, 100,slider);// new grassRateSlider(GRASS_RATE));
		slider.setFirstVal(GRASS_RATE);
		setGrassRate(GRASS_RATE);
		
	}

	public void buildSchedule ()
	{
		System.out.println("Running BuildSchedule");

		class RabbitsGrassStep extends BasicAction {
			public void execute() {
				//agent Tick
				SimUtilities.shuffle(agentList);
				for(int i =0; i < agentList.size(); i++){
					RabbitsGrassSimulationAgent rgsa = (RabbitsGrassSimulationAgent)agentList.get(i);
					rgsa.step();
				}

				//Reproducing and Dying
				int deadRabbits =remDeadRabbits();

				int newRabbits = reproduceRabbits();

				//grass Tick
				growGrass();

				displaySurf.updateDisplay();

			}

			private int reproduceRabbits() {
				int count=0,i,newEnergy;
				RabbitsGrassSimulationAgent rgsa=null,newRgsa=null;


				for (i=(agentList.size()-1);i>=0;i--)
				{
					rgsa=(RabbitsGrassSimulationAgent) agentList.get(i);
					if(rgsa.getEnergy()>birthThres)
					{
						newEnergy=rgsa.reproduceCost();

						newRgsa=new RabbitsGrassSimulationAgent(newEnergy,newEnergy);
						
						if(space.addAgent(newRgsa))
							agentList.add(newRgsa);
						//newRgsa.report();

						count++;
					}
				}
				return count;
			}

			private int remDeadRabbits() {
				int count=0,i;
				RabbitsGrassSimulationAgent rgsa=null;

				for (i=(agentList.size()-1);i>=0;i--)
				{
					rgsa=(RabbitsGrassSimulationAgent) agentList.get(i);
					if(rgsa.getEnergy()<1)
					{
						//rgsa.report();
						space.removeRabbitAt(rgsa.getX(), rgsa.getY());
						agentList.remove(i);
						count++;
					}
				}
				return count;
			}
		}

		schedule.scheduleActionBeginning(0, new RabbitsGrassStep());


		class RabbitsGrassUpdateGrassAndRabbitsInSpace extends BasicAction
		{
			public void execute() {
				amountOfGrassInSpace.step();
				amountOfRabbitsInSpace.step();
			}
		}

		schedule.scheduleActionAtInterval(10, new RabbitsGrassUpdateGrassAndRabbitsInSpace());

//		class RabbitsGrassGrowing extends BasicAction {
//		public void execute(){
//		growGrass();
//		}
//		}

//		schedule.scheduleActionAtInterval(grassRate, new RabbitsGrassGrowing());
	}

	private void growGrass()//adds 1 at the value of every grasses in the "world" 
	{
//		int j,value;
//		for(int i = 0; i < xSize; i++)
//		for(j = 0; j < ySize; j++){
//		value = space.getGrassValue(i,j);
//		if(value<MAX_GRASS_VALUE-1)
//		space.setGrassAt(i,j,value+1);
//		}

		space.spreadGrass(grassRate);
	}

	public void buildDisplay() {

		ColorMap map = new ColorMap();

		for(int i =MAX_GRASS_VALUE-1;i>0; i--){
			map.mapColor(MAX_GRASS_VALUE-i, new Color(0, (int)(i * 8 + 127), 0));
		}
		map.mapColor(0, Color.white);

		Value2DDisplay displayField = new Value2DDisplay(space.getCurrentSpace(), map);

		Object2DDisplay displayAgents = new Object2DDisplay(space.getCurrentAgentSpace());

		displayAgents.setObjectList(agentList);

		displaySurf.addDisplayableProbeable(displayField, "Field");

		displaySurf.addDisplayableProbeable(displayAgents, "Agents");

		amountOfGrassInSpace.addSequence("Grass In Space", new grassInSpace());
		amountOfRabbitsInSpace.addSequence("Rabitts In Space", new rabbitsInSpace(),new Color(0,0,254));
		
		


	}
}


